// Copyright Epic Games, Inc. All Rights Reserved.

#pragma once

#include "zenvfs.h"

#if ZEN_WITH_VFS

#	include <zencore/zencore.h>
#	include <zenbase/refcount.h>
#	include <zencore/uid.h>

#	include <string_view>
#	include <memory>
#	include <vector>

namespace zen {

struct VfsProvider;

//////////////////////////////////////////////////////////////////////////

struct VfsTreeNode;

struct VfsTreeDataSource : public RefCounted
{
	virtual void ReadNamedData(std::string_view Path, void* Buffer, uint64_t ByteOffset, uint64_t ByteCount) = 0;
	virtual void ReadChunkData(const Oid& ChunkId, void* Buffer, uint64_t ByteOffset, uint64_t ByteCount)	 = 0;
	virtual void PopulateDirectory(std::string NodePath, VfsTreeNode& DirNode)								 = 0;
};

struct VfsTreeNode
{
	inline VfsTreeNode(VfsTreeNode* InParent) : ParentNode(InParent) {}

	const std::string_view GetName() const { return Name; }
	uint64_t			   GetFileSize() const { return FileSize; }
	VfsTreeDataSource*	   GetDataSource() const { return DataSource.Get(); }
	const Oid&			   GetChunkId() const { return ChunkId; }

	const std::string GetPath() const;

	VfsTreeNode* FindNode(std::wstring_view NodeName);
	VfsTreeNode* FindNode(std::string_view NodeName);

	void AddVirtualNode(std::string_view NodeName, Ref<VfsTreeDataSource>&& DataSource);
	void AddFileNode(std::string_view NodeName, uint64_t InFileSize, Oid InChunkId, Ref<VfsTreeDataSource> DataSource = {});
	void NormalizeTree();
	bool IsDirectory() const;
	void PopulateVirtualNode();

	const std::vector<std::unique_ptr<VfsTreeNode>>& GetChildren() const { return ChildArray; }
	std::vector<std::unique_ptr<VfsTreeNode>>&		 GetChildren() { return ChildArray; }

private:
	std::string			   Name;
	uint64_t			   FileSize = 0;
	Oid					   ChunkId	= Oid::Zero;
	Ref<VfsTreeDataSource> DataSource;

	inline static const uint64_t			  DirectoryMarker = ~uint64_t(0);
	std::vector<std::unique_ptr<VfsTreeNode>> ChildArray;
	VfsTreeNode*							  ParentNode = nullptr;

	VfsTreeNode* AddNewChildNode() { return ChildArray.emplace_back(std::make_unique<VfsTreeNode>(this)).get(); }

	void GetPath(StringBuilderBase& Path) const;
};

//////////////////////////////////////////////////////////////////////////

class VfsHost
{
public:
	VfsHost(std::string_view VfsRootPath);
	~VfsHost();

	void Initialize();
	void AddMount(std::string_view Mountpoint, Ref<VfsTreeDataSource>&& DataSource);
	void Run();
	void RequestStop();
	void Cleanup();

private:
	VfsProvider* m_Provider = nullptr;
};

}  // namespace zen
#endif
